using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BendyLines
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.BackColor = Color.White;

            this.mTransitionLines = new List<TransitionLine>();
            this.mScreenThumbnails = new List<ScreenThumbnail>();
            this.mCurrentTransitionDest = Rectangle.Empty;

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | 
                ControlStyles.UserPaint | 
                ControlStyles.ResizeRedraw |
                ControlStyles.OptimizedDoubleBuffer, 
                true);

            Point center = new Point(300, 300);
            ScreenThumbnail thumb;

            thumb = this.AddScreen(new Point(center.X - 200, center.Y - 200));
            thumb.ID = 1;

            thumb = this.AddScreen(new Point(center.X + 200, center.Y - 200));
            thumb.ID = 3;

            thumb = this.AddScreen(new Point(center.X, center.Y));
            thumb.ID = 5;

            thumb = this.AddScreen(new Point(center.X - 200, center.Y + 200));
            thumb.ID = 7;

            thumb = this.AddScreen(new Point(center.X + 200, center.Y + 200));
            thumb.ID = 9;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (!e.ClipRectangle.IsEmpty)
            {
                e.Graphics.SetClip(e.ClipRectangle);
            }

            e.Graphics.Clear(this.BackColor);
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            base.OnPaint(e);

            foreach (TransitionLine ts in this.mTransitionLines)
            {
                ts.Draw(e.Graphics);
            }

            if (this.mCurrentTransitionDest != Rectangle.Empty)
            {
                TransitionLine.DrawTransition(e.Graphics, this.mStartingScreenThumbnail.Bounds, this.mCurrentTransitionDest);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            foreach (TransitionLine tl in this.mTransitionLines)
            {
                if (tl.HitTest(e.Location))
                {
                    this.mSelectedTransitionLine = tl;
                    tl.Select();
                    break;
                }
            }
            base.OnMouseDown(e);
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (this.mSelectedTransitionLine != null)
            {
                this.mSelectedTransitionLine.Deselect();
                if (this.mSelectedTransitionLine.StretchedTooFar)
                {
                    this.mTransitionLines.Remove(this.mSelectedTransitionLine);
                }
                this.mSelectedTransitionLine = null;
                this.Invalidate();
            }
            base.OnMouseUp(e);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left )
            {
                if(this.mSelectedTransitionLine != null)
                {
                    Rectangle invalidateRect = this.mSelectedTransitionLine.BoundingBox;
                    this.mSelectedTransitionLine.StrecthTo(e.Location);
                    base.Invalidate(Rectangle.Union(this.mSelectedTransitionLine.BoundingBox, invalidateRect));
                }
            }
            else
            {
                if (this.mMouseOverTransitionLine != null)
                {
                    this.mMouseOverTransitionLine.MouseOver = false;
                }

                foreach (TransitionLine tl in this.mTransitionLines)
                {
                    if (tl.HitTest(e.Location))
                    {
                        tl.MouseOver = true;
                        this.mMouseOverTransitionLine = tl;
                        break;
                    }
                }

                if (this.mMouseOverTransitionLine != null)
                {
                    base.Invalidate(this.mMouseOverTransitionLine.BoundingBox);
                }
            }
            base.OnMouseMove(e);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Random rnd = new Random();
            this.AddScreen(new Point(rnd.Next(0, this.ClientSize.Width - 102),
                rnd.Next(10, this.ClientSize.Height - 98)));
        }

        private ScreenThumbnail AddScreen(Point pLocation)
        {
            ScreenThumbnail screenThumbnail = new ScreenThumbnail();
            screenThumbnail.MouseDown += new MouseEventHandler(screenThumbnail_MouseDown);
            screenThumbnail.MouseMove += new MouseEventHandler(screenThumbnail_MouseMove);
            screenThumbnail.MouseUp += new MouseEventHandler(screenThumbnail_MouseUp);
            screenThumbnail.DoubleClick += new EventHandler(screenThumbnail_DoubleClick);
            screenThumbnail.Location = pLocation;
            this.Controls.Add(screenThumbnail);
            this.mScreenThumbnails.Add(screenThumbnail);
            return screenThumbnail;
        }

        private ScreenThumbnail HitTest(Point pLocation)
        {
            foreach (ScreenThumbnail st in this.mScreenThumbnails)
            {
                if (st.Bounds.Contains(pLocation))
                {
                    return st;
                }
            }
            return null;
        }

        private Point FindParentLocation(Control pChild, Point pLocation)
        {
            while (pChild != null)
            {
                if (pChild == this)
                {
                    return pLocation;
                }
                pLocation.X += pChild.Left;
                pLocation.Y += pChild.Top;
                pChild = pChild.Parent;
            }
            return Point.Empty;
        }

        private void screenThumbnail_DoubleClick(object sender, EventArgs e)
        {
            ScreenThumbnail thumb = sender as ScreenThumbnail;
            if (thumb != null)
            {
                this.Controls.Remove(thumb);
                this.mScreenThumbnails.Remove(thumb);
                for (int i = this.mTransitionLines.Count - 1; i >= 0; i--)
                {
                    if (this.mTransitionLines[i].StartThumbnail == thumb || this.mTransitionLines[i].EndThumbnail == thumb)
                    {
                        this.mTransitionLines.Remove(this.mTransitionLines[i]);
                    }
                }
                this.Invalidate();
            }
        }

        private void screenThumbnail_MouseUp(object sender, MouseEventArgs e)
        {
            // find the final screen thumbnail
            bool bInvalidate = (this.mCurrentTransitionDest != Rectangle.Empty);
            Point pos = FindParentLocation(sender as Control, e.Location);
            ScreenThumbnail start = this.mStartingScreenThumbnail;
            ScreenThumbnail end = HitTest(pos);
            this.mStartingScreenThumbnail = null;
            this.mCurrentTransitionDest = Rectangle.Empty;

            if ((end != null) && (start != null) && (start != end))
            {
                end.BackColor = Color.White;

                // make sure such a transition doesnt already exist
                foreach (TransitionLine ts in this.mTransitionLines)
                {
                    if ((ts.StartThumbnail == start) && (ts.EndThumbnail == end))
                    {
                        base.Invalidate();
                        return;
                    }
                }

                // now create it
                this.mTransitionLines.Add(new TransitionLine(start, end));

                // and invalidate the screen
                base.Invalidate();
            }
        }

        private void screenThumbnail_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left)
            {
                return;
            }
            if (this.mStartingScreenThumbnail == null)
            {
                return;
            }

            Point pos = FindParentLocation(sender as Control, e.Location);
            Rectangle invalidateRect = Rectangle.Union(this.mCurrentTransitionDest, this.mStartingScreenThumbnail.Bounds);
            invalidateRect.Inflate(20, 20);

            ScreenThumbnail hit = HitTest(pos);
            if (hit != null)
            {
                this.mCurrentTransitionDest = hit.Bounds;
            }
            else
            {
                this.mCurrentTransitionDest = Rectangle.FromLTRB(pos.X, pos.Y, pos.X + 1, pos.Y + 1);
            }

            // invalidate the rectangle
            this.Invalidate(invalidateRect);
        }

        private void screenThumbnail_MouseDown(object sender, MouseEventArgs e)
        {
            Console.WriteLine("---");
            this.mStartingScreenThumbnail = sender as ScreenThumbnail;
        }

        private ScreenThumbnail mStartingScreenThumbnail;
        private List<TransitionLine> mTransitionLines;
        private List<ScreenThumbnail> mScreenThumbnails;
        private Rectangle mCurrentTransitionDest;
        private TransitionLine mSelectedTransitionLine;
        private TransitionLine mMouseOverTransitionLine;
    }
}